<script>
import { GlSkeletonLoader, GlAlert, GlSprintf } from '@gitlab/ui';
import vulnerabilityBlobInfoQuery from 'ee/security_dashboard/graphql/queries/vulnerability_blob_info.query.graphql';
import CodeBlockHighlighted from '~/vue_shared/components/code_block_highlighted.vue';

export default {
  components: { GlSkeletonLoader, GlAlert, GlSprintf, CodeBlockHighlighted },
  inject: ['projectFullPath'],
  props: {
    location: { type: Object, required: true },
  },
  data() {
    return {
      blobInfo: null,
      loadingError: false,
    };
  },
  apollo: {
    blobInfo: {
      query: vulnerabilityBlobInfoQuery,
      variables() {
        return {
          projectPath: this.projectFullPath,
          filePath: this.location.file,
          // The ref (commit SHA) is important. We need to get the blob info for the commit that the vulnerability was
          // found in, because the file could've been changed or deleted on the default branch.
          ref: this.ref,
        };
      },
      update({ project }) {
        return project.repository.blobs.nodes[0];
      },
      error() {
        this.loadingError = true;
      },
    },
  },
  computed: {
    startLine() {
      return this.location.startLine;
    },
    endLine() {
      return this.location.endLine || this.startLine;
    },
    lineNumbers() {
      const numbers = [];
      for (let i = this.startLine; i <= this.endLine; i += 1) {
        numbers.push(i);
      }

      return numbers;
    },
    sourceCodeLines() {
      // rawTextBlob is the entire file, the GraphQL query does not have the ability to get only certain lines of the
      // file. Because we only need certain lines, we'll split the lines into an array, slice out only those lines, then
      // rejoin it back into a string so that the code block component can render it properly.
      const lines = this.blobInfo.rawTextBlob.split('\n');
      return lines.slice(this.startLine - 1, this.endLine).join('\n');
    },
    ref() {
      // Matches the 40-character hex string of the Git SHA in the blob path, for example:
      // "/group/project/-/blob/cdeda7ae724a332e008d17245209d5edd9ba6499/src/file.js"
      // will match "cdeda7ae724a332e008d17245209d5edd9ba6499".
      return this.location.blobPath.match(/blob\/([a-f0-9]{40})/)[1] || '';
    },
    userColorTheme() {
      return gon.user_color_scheme;
    },
  },
};
</script>

<template>
  <div
    v-if="$apollo.queries.blobInfo.loading"
    :class="userColorTheme"
    class="code gl-p-3 gl-pb-2 gl-rounded-base"
  >
    <gl-skeleton-loader />
  </div>

  <gl-alert
    v-else-if="loadingError"
    :dismissible="false"
    variant="danger"
    data-testid="loading-error"
  >
    {{ s__('Vulnerability|Something went wrong while trying to get the source file.') }}
  </gl-alert>

  <gl-alert
    v-else-if="!blobInfo"
    :dismissible="false"
    variant="warning"
    data-testid="file-not-found-warning"
  >
    <gl-sprintf :message="s__('Vulnerability|%{file} was not found in commit %{ref}')">
      <template #file>
        <code>{{ location.file }}</code>
      </template>
      <template #ref>
        <code>{{ ref }}</code>
      </template>
    </gl-sprintf>
  </gl-alert>

  <div v-else class="file-holder">
    <div
      :class="userColorTheme"
      class="code blob-viewer file-content gl-display-flex gl-align-items-center gl-overflow-auto gl-rounded-base"
    >
      <div
        class="line-numbers line-links diff-line-num gl-absolute gl-z-index-3 gl-p-0! gl-hover-bg-transparent! gl-border-r"
        data-testid="line-numbers"
      >
        <a
          v-for="lineNumber in lineNumbers"
          :key="lineNumber"
          class="file-line-num gl-hover-text-decoration-none"
        >
          {{ lineNumber }}
        </a>
      </div>
      <code-block-highlighted
        :language="blobInfo.language"
        :code="sourceCodeLines"
        class="gl-overflow-visible! gl-p-0! gl-border-none!"
      />
    </div>
  </div>
</template>
